/**
 * Modbus TCP通信模块
 * 实现基于Modbus TCP协议的PLC通信
 */
const ModbusRTU = require('modbus-serial');
const logger = require('../utils/logger');
const EventEmitter = require('events');

class ModbusTCPClient extends EventEmitter {
  constructor(options) {
    super();
    this.options = {
      host: options.ip || '127.0.0.1',
      port: options.port || 502,
      unitId: options.unitId || 1,
      timeout: options.timeout || 5000,
      autoReconnect: options.autoReconnect !== false,
      reconnectInterval: options.reconnectInterval || 5000,
      maxReconnectAttempts: options.maxReconnectAttempts || 10,
      ...options
    };
    
    this.client = new ModbusRTU();
    this.connected = false;
    this.reconnectAttempts = 0;
    this.reconnectTimer = null;
    this.deviceId = options.id || 'unknown';
  }

  /**
   * 连接到PLC设备
   */
  async connect() {
    try {
      logger.info(`[${this.deviceId}] 正在连接到Modbus TCP设备: ${this.options.host}:${this.options.port}`);
      
      await this.client.connectTCP(this.options.host, { port: this.options.port });
      this.client.setID(this.options.unitId);
      this.client.setTimeout(this.options.timeout);
      
      this.connected = true;
      this.reconnectAttempts = 0;
      
      logger.info(`[${this.deviceId}] 成功连接到Modbus TCP设备: ${this.options.host}:${this.options.port}`);
      this.emit('connected');
      
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 连接Modbus TCP设备失败: ${error.message}`);
      this.connected = false;
      
      if (this.options.autoReconnect) {
        this.scheduleReconnect();
      }
      
      this.emit('error', error);
      return false;
    }
  }

  /**
   * 断开与PLC设备的连接
   */
  async disconnect() {
    try {
      if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = null;
      }
      
      if (this.connected) {
        await this.client.close();
        this.connected = false;
        logger.info(`[${this.deviceId}] 已断开与Modbus TCP设备的连接`);
      }
      
      this.emit('disconnected');
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 断开连接失败: ${error.message}`);
      this.emit('error', error);
      return false;
    }
  }

  /**
   * 安排重新连接
   */
  scheduleReconnect() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
    }
    
    if (this.reconnectAttempts < this.options.maxReconnectAttempts) {
      this.reconnectAttempts++;
      logger.info(`[${this.deviceId}] 计划在${this.options.reconnectInterval}ms后进行第${this.reconnectAttempts}次重连尝试`);
      
      this.reconnectTimer = setTimeout(async () => {
        logger.info(`[${this.deviceId}] 尝试重新连接...`);
        await this.connect();
      }, this.options.reconnectInterval);
    } else {
      logger.error(`[${this.deviceId}] 达到最大重连尝试次数(${this.options.maxReconnectAttempts})，停止重连`);
      this.emit('reconnectFailed');
    }
  }

  /**
   * 检查连接状态
   */
  isConnected() {
    return this.connected;
  }

  /**
   * 发送心跳检测
   */
  async sendHeartbeat() {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      // 读取一个寄存器作为心跳检测
      await this.readHoldingRegisters(0, 1);
      logger.debug(`[${this.deviceId}] 心跳检测成功`);
      return true;
    } catch (error) {
      logger.warn(`[${this.deviceId}] 心跳检测失败: ${error.message}`);
      this.connected = false;
      
      if (this.options.autoReconnect) {
        this.scheduleReconnect();
      }
      
      this.emit('error', error);
      return false;
    }
  }

  /**
   * 读取线圈状态
   * @param {number} address - 起始地址
   * @param {number} quantity - 读取数量
   */
  async readCoils(address, quantity) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 读取线圈状态: 地址=${address}, 数量=${quantity}`);
      const result = await this.client.readCoils(address, quantity);
      return result.data;
    } catch (error) {
      logger.error(`[${this.deviceId}] 读取线圈状态失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 读取离散输入状态
   * @param {number} address - 起始地址
   * @param {number} quantity - 读取数量
   */
  async readDiscreteInputs(address, quantity) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 读取离散输入状态: 地址=${address}, 数量=${quantity}`);
      const result = await this.client.readDiscreteInputs(address, quantity);
      return result.data;
    } catch (error) {
      logger.error(`[${this.deviceId}] 读取离散输入状态失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 读取保持寄存器
   * @param {number} address - 起始地址
   * @param {number} quantity - 读取数量
   */
  async readHoldingRegisters(address, quantity) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 读取保持寄存器: 地址=${address}, 数量=${quantity}`);
      console.log('读取保持寄存器 readHoldingRegisters:', address, quantity);
      
      const result = await this.client.readHoldingRegisters(address, quantity);
      return result.data;
    } catch (error) {
      console.log('读取保持寄存器 readHoldingRegisters catch error:', error);
      logger.error(`[${this.deviceId}] 读取保持寄存器失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 读取输入寄存器
   * @param {number} address - 起始地址
   * @param {number} quantity - 读取数量
   */
  async readInputRegisters(address, quantity) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 读取输入寄存器: 地址=${address}, 数量=${quantity}`);
      const result = await this.client.readInputRegisters(address, quantity);
      return result.data;
    } catch (error) {
      logger.error(`[${this.deviceId}] 读取输入寄存器失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 写入单个线圈
   * @param {number} address - 线圈地址
   * @param {boolean} value - 写入值
   */
  async writeSingleCoil(address, value) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 写入单个线圈: 地址=${address}, 值=${value}`);
      await this.client.writeCoil(address, value);
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 写入单个线圈失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 写入多个线圈
   * @param {number} address - 起始地址
   * @param {Array<boolean>} values - 写入值数组
   */
  async writeMultipleCoils(address, values) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 写入多个线圈: 地址=${address}, 值数量=${values.length}`);
      await this.client.writeCoils(address, values);
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 写入多个线圈失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 写入单个寄存器
   * @param {number} address - 寄存器地址
   * @param {number} value - 写入值
   */
  async writeSingleRegister(address, value) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 写入单个寄存器: 地址=${address}, 值=${value}`);
      await this.client.writeRegister(address, value);
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 写入单个寄存器失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }

  /**
   * 写入多个寄存器
   * @param {number} address - 起始地址
   * @param {Array<number>} values - 写入值数组
   */
  async writeMultipleRegisters(address, values) {
    try {
      if (!this.connected) {
        throw new Error('设备未连接');
      }
      
      logger.debug(`[${this.deviceId}] 写入多个寄存器: 地址=${address}, 值数量=${values.length}`);
      await this.client.writeRegisters(address, values);
      return true;
    } catch (error) {
      logger.error(`[${this.deviceId}] 写入多个寄存器失败: ${error.message}`);
      
      if (error.message.includes('Timed out') || error.message.includes('Port is closed')) {
        this.connected = false;
        if (this.options.autoReconnect) {
          this.scheduleReconnect();
        }
      }
      
      this.emit('error', error);
      throw error;
    }
  }
}

module.exports = ModbusTCPClient;
